skip to content
logo

Search

Svg Icon 图标组件开发

Why ? 🤔

为什么使用 SVG 而不是 IconFont,可以看这篇文章 Inline SVG vs Icon Fonts 

IconFont 主要有以下几点劣势:

  1. 浏览器将其视为文字进行抗锯齿优化,有时得到的效果并没有想象中那么锐利。 尤其是在不同系统下对文字进行抗锯齿的算法不同,可能会导致显示效果不同
  2. IconFont 作为一种字体,Icon 显示的大小和位置可能要受到 font-sizeline-heightword-spacing  等等 CSS 属性的影响。 Icon 所在容器的 `CSS` 样式可能对 `Icon` 的位置产生影响,调整起来很不方便
  3. 使用上存在不便。首先,加载一个包含数百个图标的IconFont,却只使用其中几个,非常浪费加载时间。 自己制作IconFont以及把多个IconFont 中用到的图标整合成一个 Font 也非常不方便
  4. 如果想实现最大程度的浏览器支持,可能要提供至少四种不同类型的字体文件。包括TTFWOFFEOT以及一个使用SVG格式定义的字体
  5. 网络延时会导致 Icon 会先加载出来一个 string

Svg Icon 的优劣势:

  1. 完全离线化使用,不需要从 CDN 下载字体文件,图标不会因为网络问题呈现方块,也无需字体文件本地部署
  2. 在低端设备上 SVG 有更好的清晰度
  3. 支持多色图标
  4. 对于内建图标的更换可以提供更多 API,而不需要进行样式覆盖

劣势:兼容性(其实目前浏览器兼容性已经不错 查看兼容性


What ? 🧐

实现原理

  • svg 图标比较小,而且都是可读的 xml 文本,我们把它直接放在项目中即可,通过vite-plugin-svg-icons 插件生成 svg 雪碧图,实现自动引入
  • 插件会自动将所有 svg 图片加载到 HTML 中。并且每一个 svg 将会被过滤去无用的信息数据。让 svg 达到最小的值。之后使用 svg 图片就只需要操作 DOM 即可,而不需要发送 http 请求
  • 利用 svg 的 symbol 元素,将每个 icon 包括在 symbol 中
  • 再通过 <use xlink:href="symbolId"/> 来使用所需的 icon

优点:

  • 解决各种版本 iconfont 私有图标库问题
  • 每个 SVG 图标都可以更改大小颜色
  • 在页面中使用<svgIcon name="home" />,代码清爽

How ? 😉

  1. 配置 vite.config.ts 文件

    安装 vite-plugin-svg-icons 插件后编辑 Vite 配置文件:文档

    • iconDirs 项目中 svg 图标存放路径,这里设置为 assets 下的 icons 文件夹
    • symbolId 图标的唯一 id 标识,dir 代表图标的文件夹名称
    • 比如在 icons 中将登录模块相关的图标都整理到 login 文件夹中,则这里的 dir 就是 login
    import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
    // 省略其他部分代码
    plugins: [
        createSvgIconsPlugin({
           iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
           // 图标 ID 样式 dir 代表文件的文件夹名称
    			 symbolId: 'svg-icon-[dir]-[name]',
        }),
    ]

    虽然用文件夹来区分已经可以很大程度避免重名问题了,但是也会出现iconDirs包含多个文件夹,且文件名一样的 svg,这个需要开发者自己规避下。

  2. 配置 main.ts

    • import 'virtual:svg-icons-register'
  3. 封装 SvgIcon 图标组件

    <template>
      <div :class="wrapperColor">
        <svg
    			aria-hidden="true"
    			:width="width || size"
    			:height="height || size"
    			:class="className">
    	    <use :xlink:href="symbolId" :fill="color" />
        </svg>
      </div>
    </template>
    
    <script setup lang="ts" name="SvgIcon">
    interface Props {
    	prefix?: string
    	name?: string
    	color?: string
    	size?: number
    	width?: number
    	height?: number
    	className?: string
    	dir?: string
    }
    
    const props = withDefaults(defineProps<Props>(), {
    	prefix: 'svg-icon',
    	name: '',
    	color: '#fff',
    	size: 16,
    	width: 16,
    	height: 16,
    	className: 'svg-icon',
    	dir: '',
    })
    
    const wrapperColor = computed(() => `text-color-[${props.color}]`)
    const symbolId = computed(() => {
    	return props.dir?.length
    		? `#${props.prefix}-${props.dir}-${props.name}`
    		: `#${props.prefix}-${props.name}`
    })
    </script>
    
    <style lang="scss" scoped>
    .svg-icon {
    	vertical-align: -0.15em;
    	fill: currentColor;
    	overflow: hidden;
    }
    </style>
    • 通过 wrapperColor (Windi Css 功能类)将颜色设置给 svg 文件,默认白色
  4. 使用说明

    <svg-icon name="user" color="#2395f1" :size="30"/>
    <svg-icon name="action" color="#d9363e" :width="40" :height="30"/>
    
    <div class="text-green-500">
      文本
    	<svg-icon name="robot" dir="timeline"  className="pr-10px" @click="test"/>
    	<svg-icon name="action" color="#d9363e" />
    </div>
    • name 就是放置在 @/assets/icons 文件夹里的图标文件名
    • color 颜色填充,使用此项会默认覆盖图标颜色
    • size 为图标的宽高值,也可以单独声明宽高的值,优先级更高
    • 如果想实现多个图标批量设置色值,可以通过给父元素设置 color 色值实现,不过优先级比单独给组件设置 color 要低
    • 如果图标是放在指定的文件夹内,需要使用 dir 标识
    • 图标的其他样式,可以通过额外设置 className 实现
    • 因为图标组件中是用 div 标签包裹 svg 的,可以直接给组件绑定事件

    注意事项: 🚨

    • 单色图标需要设计同学导出 fill ="currentColor" 属性的 svg 图片才能实现自定义颜色
    • 如果 UI 没有提供,可以在 Figma中安装 Svg Export 插件,将导出配置中的 Use currentColor as fillRemove all fills 选项打开